#pragma once

#include <iostream>
#include <string>
#include<unistd.h>
#include<errno.h>
#include<cstring>
#include<cstdlib>
#include<functional>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>

#define gnum 1024
namespace Serevr
{
    using namespace std;
    using func_t  = std::function<void(int,string,uint16_t,string)>;
    static const string defaultIp = "0.0.0.0";// TODO

    enum{USAGE_ERR = 1,SOCKET_ERR,BIND_ERR,OPRN_ERR};

    class udpServer
    {
    public:
        udpServer(const func_t cb,const uint16_t& port,const string& ip = defaultIp)
        :_callback(cb),_port(port),_ip(ip),_sockfd(-1)
        {}
        void initServer()
        {
            // 1.创建套接字 -- 文件
            _sockfd = socket(AF_INET,SOCK_DGRAM,0);// 网络通信，UDP ； AF_INET：这里是我要创建网络通信的套接字
            if(_sockfd == -1)
            {
                cerr << "socket error :" << errno << strerror(errno) << endl;
                exit(SOCKET_ERR); // 失败了就不玩了
            }
            cout << "create socket success :" << _sockfd << endl;
            // 2.绑定port，ip -- 网络
            struct sockaddr_in local;// 定义了一个变量，栈，用户
            bzero(&local,sizeof(local));
            local.sin_family = AF_INET;// 协议家族 ； AF_INET：填充这样的结构体
            local.sin_port = htons(_port); // 我要给别人发送信息，我也要把ip+port发给对方
            // local.sin_addr.s_addr = inet_addr(_ip.c_str()); // string->uint32_t 2.htonl();
            // 这台服务器多个ip，这要是发送给这台服务器的信息都能接收
            local.sin_addr.s_addr = htonl(INADDR_ANY);// 任意地址，bind，服务器的真实写法
            int n = bind(_sockfd,(struct sockaddr*)&local,sizeof(local));
            if(n == -1)
            {
                cerr << "bind error :" << errno << strerror(errno) << endl;
                exit(BIND_ERR); // 失败了就不玩了
            }
            cout << "bind socket success "  << endl;

            // UDP Server 的预备工作完成
        }
        void start()
        {
            // 服务器的本质其实就是一个死循环
            char buffer[gnum];
            for(;;)
            {
                // 读取数据
                struct sockaddr_in peer;
                socklen_t len = sizeof(peer); //socklen_t:就是uint32_t
                ssize_t s = recvfrom(_sockfd,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&peer,&len);//要让接口知道是什么类型的长度
                // 1.数据是什么 2.谁发的？
                if(s > 0)
                {
                    buffer[s] = 0;
                    string clientip = inet_ntoa(peer.sin_addr);// 1.网络序列 2.int->点分十进制IP
                    uint16_t clientport = ntohs(peer.sin_port);
                    string message = buffer;
                    cout << clientip << "[" << clientport << "]# " << message << endl;
                    //对数据进行处理
                    _callback(_sockfd,clientip,clientport,message);
                }
            }
        }
        ~udpServer()
        {
        }
    private:
        uint16_t _port; // 0~65535
        string _ip;// 只是可读性好 uint32_t:电脑用起来好 // 实际上，不建议绑定一个明确的ip
        int _sockfd;
        func_t _callback;
    };
}